/*
 * Copyright (c) 2010-2013 Mark Allen.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.faceboob.api.restfb.example;

import static java.lang.String.format;
import static java.lang.System.currentTimeMillis;
import static java.lang.System.out;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.restfb.Connection;
import com.restfb.DefaultFacebookClient;
import com.restfb.DefaultJsonMapper;
import com.restfb.Facebook;
import com.restfb.FacebookClient;
import com.restfb.JsonMapper;
import com.restfb.Parameter;
import com.restfb.json.JsonArray;
import com.restfb.json.JsonObject;
import com.restfb.types.Page;
import com.restfb.types.Post;
import com.restfb.types.Url;
import com.restfb.types.User;

/**
 * Examples of RestFB's Graph API functionality.
 * 
 * @author <a href="http://restfb.com">Mark Allen</a>
 */
@SuppressWarnings("deprecation")
public class GraphReaderExample extends Example {
	/**
	 * RestFB Graph API client.
	 */
	private final FacebookClient facebookClient;

	/**
	 * Entry point. You must provide a single argument on the command line: a valid Graph API access token.
	 * 
	 * @param args
	 *            Command-line arguments.
	 * @throws IllegalArgumentException
	 *             If no command-line arguments are provided.
	 */
	public static void main(final String[] args) {

		new GraphReaderExample(
				"BAAHXKxGP43MBABI7JCP7DGjjsz20S3IAZCfNZA2jnYppVQFfjFh6pJQAgkwaq7Wqg3zMi4O7jsrMKAsZCCRISVZCIFULVSqOhezLe9Sx8uZCZBBluSaP3dsJ8zL1NYTdHzPlUYSejsSSAohHKfqRHElvRZB2FwvaLyjQpaYHFIWA6njgelkViRgvKeZCmaPaqZC49QsIuSZCIP9Vn2eX7KvkbhIDlsRNxX5jYZD")
				.runEverything();
	}

	GraphReaderExample(final String accessToken) {
		this.facebookClient = new DefaultFacebookClient(accessToken);
	}

	void runEverything() {
		this.fetchObject();
		// fetchObjects();
		// fetchObjectsAsJsonObject();
		// fetchConnections();
		// fetchDifferentDataTypesAsJsonObject();
		// query();
		// multiquery();
		// search();
		// metadata();
		// paging();
		// selection();
		// parameters();
		// rawJsonResponse();
	}

	void fetchObject() {
		out.println("* Fetching single objects *");

		final User user = this.facebookClient.fetchObject("me", User.class);
		final Page page = this.facebookClient.fetchObject("JavaSupport", Page.class);

		out.println("User name: " + user.getName());
		out.println("Page likes: " + page.getLink());

	}

	void fetchObjectsAsJsonObject() {
		out.println("* Fetching multiple objects at once as a JsonObject *");

		final List<String> ids = new ArrayList<String>();
		ids.add("btaylor");
		ids.add("http://www.imdb.com/title/tt0117500/");

		// Make the API call
		final JsonObject results = this.facebookClient.fetchObjects(ids, JsonObject.class);

		// Pull out JSON data by key and map each type by hand.
		final JsonMapper jsonMapper = new DefaultJsonMapper();
		final User user = jsonMapper.toJavaObject(results.getString("btaylor"), User.class);
		final Url url = jsonMapper.toJavaObject(results.getString("http://www.imdb.com/title/tt0117500/"), Url.class);

		out.println("User is " + user);
		out.println("URL is " + url);
	}

	void fetchObjects() {
		out.println("* Fetching multiple objects at once *");

		final FetchObjectsResults fetchObjectsResults = this.facebookClient.fetchObjects(Arrays.asList("me", "cocacola"),
				FetchObjectsResults.class);

		out.println("User name: " + fetchObjectsResults.me.getName());
		out.println("Page likes: " + fetchObjectsResults.page.getLikes());
	}

	void fetchDifferentDataTypesAsJsonObject() {
		out.println("* Fetching different types of data as JsonObject *");

		final JsonObject btaylor = this.facebookClient.fetchObject("btaylor", JsonObject.class);
		out.println(btaylor.getString("name"));

		final JsonObject photosConnection = this.facebookClient.fetchObject("me/photos", JsonObject.class);
		final JsonArray photosConnectionData = photosConnection.getJsonArray("data");

		if (photosConnectionData.length() > 0) {
			final String firstPhotoUrl = photosConnectionData.getJsonObject(0).getString("source");
			out.println(firstPhotoUrl);
		}

		final String query = "SELECT uid, name FROM user WHERE uid=220439 or uid=7901103";
		final List<JsonObject> queryResults = this.facebookClient.executeQuery(query, JsonObject.class);

		if (queryResults.size() > 0) {
			out.println(queryResults.get(0).getString("name"));
		}
	}

	/**
	 * Holds results from a "fetchObjects" call.
	 */
	public static class FetchObjectsResults {
		@Facebook
		User me;

		@Facebook(value = "cocacola")
		Page page;
	}

	void fetchConnections() {
		out.println("* Fetching connections *");

		final Connection<User> myFriends = this.facebookClient.fetchConnection("me/friends", User.class);
		final Connection<Post> myFeed = this.facebookClient.fetchConnection("me/feed", Post.class);

		out.println("Count of my friends: " + myFriends.getData().size());

		if (myFeed.getData().size() > 0) {
			out.println("First item in my feed: " + myFeed.getData().get(0).getMessage());
		}
	}

	void query() {
		out.println("* FQL Query *");

		final List<FqlUser> users = this.facebookClient.executeQuery("SELECT uid, name FROM user WHERE uid=220439 or uid=7901103",
				FqlUser.class);

		out.println("User: " + users);
	}

	void multiquery() {
		out.println("* FQL Multiquery *");

		final Map<String, String> queries = new HashMap<String, String>();
		queries.put("users", "SELECT uid, name FROM user WHERE uid=220439 OR uid=7901103");
		queries.put("likers", "SELECT user_id FROM like WHERE object_id=122788341354");

		final MultiqueryResults multiqueryResults = this.facebookClient.executeMultiquery(queries, MultiqueryResults.class);

		out.println("Users: " + multiqueryResults.users);
		out.println("People who liked: " + multiqueryResults.likers);
	}

	/**
	 * Holds results from an "executeQuery" call.
	 * <p>
	 * Be aware that FQL fields don't always map to Graph API Object fields.
	 */
	public static class FqlUser {
		@Facebook
		String uid;

		@Facebook
		String name;

		@Override
		public String toString() {
			return format("%s (%s)", this.name, this.uid);
		}
	}

	/**
	 * Holds results from an "executeQuery" call.
	 * <p>
	 * Be aware that FQL fields don't always map to Graph API Object fields.
	 */
	public static class FqlLiker {
		@Facebook("user_id")
		String userId;

		@Override
		public String toString() {
			return this.userId;
		}
	}

	/**
	 * Holds results from a "multiquery" call.
	 */
	public static class MultiqueryResults {
		@Facebook
		List<FqlUser> users;

		@Facebook
		List<FqlLiker> likers;
	}

	void search() {
		out.println("* Searching connections *");

		final Connection<Post> publicSearch = this.facebookClient.fetchConnection("search", Post.class, Parameter.with("q", "watermelon"),
				Parameter.with("type", "post"));

		final Connection<User> targetedSearch = this.facebookClient.fetchConnection("me/home", User.class, Parameter.with("q", "Mark"),
				Parameter.with("type", "user"));

		if (publicSearch.getData().size() > 0) {
			out.println("Public search: " + publicSearch.getData().get(0).getMessage());
		}

		out.println("Posts on my wall by friends named Mark: " + targetedSearch.getData().size());
	}

	void metadata() {
		out.println("* Metadata *");

		final User userWithMetadata = this.facebookClient.fetchObject("me", User.class, Parameter.with("metadata", 1));

		out.println("User metadata: has albums? " + userWithMetadata.getMetadata().getConnections().hasAlbums());
	}

	void paging() {
		out.println("* Paging support *");

		final Connection<User> myFriends = this.facebookClient.fetchConnection("me/friends", User.class);
		final Connection<Post> myFeed = this.facebookClient.fetchConnection("me/feed", Post.class, Parameter.with("limit", 100));

		out.println("Count of my friends: " + myFriends.getData().size());

		if (myFeed.getData().size() > 0) {
			out.println("First item in my feed: " + myFeed.getData().get(0));
		}

		for (final List<Post> myFeedConnectionPage : myFeed) {
			for (final Post post : myFeedConnectionPage) {
				out.println("Post from my feed: " + post);
			}
		}
	}

	void selection() {
		out.println("* Selecting specific fields *");

		final User user = this.facebookClient.fetchObject("me", User.class, Parameter.with("fields", "id,name"));

		out.println("User name: " + user.getName());
	}

	void parameters() {
		out.println("* Parameter support *");

		final Date oneWeekAgo = new Date(currentTimeMillis() - (1000L * 60L * 60L * 24L * 7L));

		final Connection<Post> filteredFeed = this.facebookClient.fetchConnection("me/feed", Post.class, Parameter.with("limit", 3),
				Parameter.with("until", "yesterday"), Parameter.with("since", oneWeekAgo));

		out.println("Filtered feed count: " + filteredFeed.getData().size());
	}

	void rawJsonResponse() {
		out.println("* Raw JSON *");
		out.println("User object JSON: " + this.facebookClient.fetchObject("me", String.class));
	}
}